package servidor;

import java.util.ArrayList;
import java.util.List;

import org.omg.CORBA.ORB;
import org.omg.CORBA.ORBPackage.InvalidName;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContext;
import org.omg.CosNaming.NamingContextHelper;
import org.omg.CosNaming.NamingContextPackage.CannotProceed;
import org.omg.CosNaming.NamingContextPackage.NotFound;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;
import org.omg.PortableServer.POAManagerPackage.AdapterInactive;
import org.omg.PortableServer.POAPackage.ServantNotActive;
import org.omg.PortableServer.POAPackage.WrongPolicy;

import ClienteInterfaces.ClienteInterface;
import ClienteInterfaces.ClienteInterfaceHelper;
import ServidorInterfaces.ServidorInterfacePOA;

public class Servidor extends ServidorInterfacePOA {

	//Lista com os Clientes cadastrado
	private List<String> nomeLista;
	private ArrayList<String> usuarioLista;
	private List<String> senhaLista;
	private ArrayList<Double> longitudeDoCliente;
	private ArrayList<Double> latitudeDoCliente;
	private ArrayList<String> statusDoCliente;
	private ArrayList<String> fotoCliente;

	private String messageBuffer;

	private org.omg.CORBA.Object refClients;
	ORB orb;
	public Servidor(String[] args) {
		usuarioLista = new ArrayList<String>();
		latitudeDoCliente = new ArrayList<Double>();
		longitudeDoCliente = new ArrayList<Double>();
		try {
			orb = init_name_server(args);
			System.out.println("Servidor Pronto ...");
			orb.run();
		} catch (Exception ex) {
			System.out.println("Erro");
			ex.printStackTrace();
		}

	}

	//Cadastra o serviço no servidor de nomes
	private ORB init_name_server(String[] args) throws InvalidName,
	ServantNotActive, WrongPolicy, NotFound, CannotProceed,
	org.omg.CosNaming.NamingContextPackage.InvalidName, AdapterInactive {

		ORB orb = ORB.init(args, null);
		org.omg.CORBA.Object objPoa = orb.resolve_initial_references("RootPOA");
		POA rootPOA = POAHelper.narrow(objPoa);
		org.omg.CORBA.Object obj = orb.resolve_initial_references("NameService");
		NamingContext naming = NamingContextHelper.narrow(obj);

		org.omg.CORBA.Object objRef = rootPOA.servant_to_reference(this);
		NameComponent[] name = { new NameComponent("Chat", "msn_corba") };
		naming.rebind(name, objRef);

		rootPOA.the_POAManager().activate();
		return orb;
	}

	//Envia a mensagem a todos os clientes cadastrados
	public void broadCastReceivedMessage(String message)
	{
		//Para enviar a todos os clientes 
		for (String current : usuarioLista) {
			org.omg.CORBA.Object obj;
			try {
				obj = orb.resolve_initial_references("NameService");
				NamingContext naming = NamingContextHelper.narrow(obj);
				//Mostra no servidor quem está se comunicando
				System.out.println(current+" está recebendo uma mensagem");
				// 
				NameComponent[] name = { new NameComponent(current, "msn_corba") };
				refClients = naming.resolve(name);
				ClienteInterface client = ClienteInterfaceHelper.narrow(refClients);
				//Envia a mensagem ao cliente atual
				client.receiveDataFromServer(message);
			} catch (InvalidName e) {
				e.printStackTrace();
			} catch (NotFound e) {
				e.printStackTrace();
			} catch (CannotProceed e) {
				e.printStackTrace();
			} catch (org.omg.CosNaming.NamingContextPackage.InvalidName e) {
				e.printStackTrace();
			}

		}

	}

	public void sendMessage(String arg2) {
		if (messageBuffer == null)
			messageBuffer =  new String();
		messageBuffer+="\n"+arg2;// TODO histórico da conversa
		broadCastReceivedMessage(arg2);

	}

	public static void main(String[] args) {

		Servidor servidor = new Servidor(args);
	}

	//tem que tratar se o usuário nao existir
	public void enviarMensagem(String nomeUsuario, String msg, String nomeUsuarioQueEnviou) {
		// TODO Auto-generated method stub
		org.omg.CORBA.Object obj5;

		try {
			obj5 = orb.resolve_initial_references("NameService");
			NamingContext naming5 = NamingContextHelper.narrow(obj5);
			//Mostra no servidor quem está se comunicando
			System.out.println(nomeUsuario+" está recebendo uma mensagem");
			//int ind = indiceCliente(nomeUsuario);
			//Mostra onde o usuário está
			//System.out.println("Latitude:"+latitudeDoCliente.get(ind)+"Longitude:"+longitudeDoCliente.get(ind));
			// 
			System.out.println("A quem irá ser enviado a mesnsagem: "+nomeUsuario);
			NameComponent[] name5 = { new NameComponent(nomeUsuario, "msn_corba") };
			org.omg.CORBA.Object refClients5 = naming5.resolve(name5);
			ClienteInterface client = ClienteInterfaceHelper.narrow(refClients5);
			//Envia a mensagem ao cliente atual
			client.receiveDataFromServer(msg);
			client.recebeDadosDoServidor(nomeUsuarioQueEnviou, msg,fotoCliente.get(usuarioLista.indexOf(nomeUsuarioQueEnviou)));

		} catch (InvalidName e) {
			e.printStackTrace();
		} catch (NotFound e) {
			e.printStackTrace();
		} catch (CannotProceed e) {
			e.printStackTrace();
		} catch (org.omg.CosNaming.NamingContextPackage.InvalidName e) {
			e.printStackTrace();
		}

	}

	public ClienteInterface referenciaDoCliente(String nom) {
		org.omg.CORBA.Object obj;
		try {
			obj = orb.resolve_initial_references("NameService");

			NamingContext naming = NamingContextHelper.narrow(obj);
			//Mostra no servidor quem está se comunicando
			//System.out.println(nom+" está recebendo uma mensagem");
			//int ind = indiceCliente(nom);
			//Mostra onde o usuário está
			//System.out.println("Latitude:"+latitudeDoCliente.get(ind)+"Longitude:"+longitudeDoCliente.get(ind));
			// 
			NameComponent[] name = { new NameComponent(nom, "msn_corba") };
			refClients = naming.resolve(name);
			ClienteInterface cliente = ClienteInterfaceHelper.narrow(refClients);

			return cliente;

		} catch (InvalidName e) {
			e.printStackTrace();
		} catch (NotFound e) {
			e.printStackTrace();
		} catch (CannotProceed e) {
			e.printStackTrace();
		} catch (org.omg.CosNaming.NamingContextPackage.InvalidName e) {
			e.printStackTrace();
		}
		return null;
	}

	//Procura o cliente pelo nome e -1 se nao existir
	public int indiceCliente(String nom) {

		for (int i = 0; i < usuarioLista.size(); i++) {
			if(usuarioLista.get(i).equals(nom)){
				return i;
			}
		}
		return -1;		
	}

	//Cadastra o cliente no servidor
	public void insertClient(String arg1) {
		// TODO Auto-generated method stub
		if (usuarioLista==null)
			usuarioLista= new ArrayList<String>();

		//Adiciona o nome dele no servidor
		usuarioLista.add(arg1);
	}

	//Cadastra usuário com a sua localização
	public boolean cadastroCliente(String nomeCliente, String usuario, String senha, String foto) {
		if (nomeLista == null){
			nomeLista= new ArrayList<String>();
		}
		if (usuarioLista == null){
			usuarioLista= new ArrayList<String>();
		}
		if (senhaLista == null){
			senhaLista= new ArrayList<String>();
		}
		if (latitudeDoCliente == null){
			latitudeDoCliente = new ArrayList<Double>();
		}
		if(statusDoCliente==null){
			statusDoCliente = new ArrayList<String>();
		}
		if(longitudeDoCliente == null) {
			longitudeDoCliente = new ArrayList<Double>();
		}
		if(fotoCliente == null) {
			fotoCliente = new ArrayList<String>();
		}
		System.out.println("Indice do usuário "+usuarioLista.indexOf(usuario));

		//Se usuario não existir na lista 
		if( usuarioLista.indexOf(usuario) == -1 ) {
			//Adiciona o nome dele no servidor
			nomeLista.add(nomeCliente);
			usuarioLista.add(usuario);
			senhaLista.add(senha);
			latitudeDoCliente.add(null);
			longitudeDoCliente.add(null);
			statusDoCliente.add("offline");
			fotoCliente.add(foto);
			//Manda todos os outros clientes atualizarem suas telas principais
			//atualizarTodos(usuario);
			return true;
		} else { //se o usuário já existir
			return false;
		}

	}

	//Faz Login do usuario
	public boolean fazLogin(String usuario, String senha, double latitude, double longitude, String statusCliente) {

		System.out.println("Usuario identificado num: "+usuarioLista.indexOf(usuario));
		System.out.println("Usuario passado: "+usuario);
		System.out.println("Senha passada pela funcao: "+senha);
		if( usuarioLista.indexOf(usuario) != -1 ) {
			System.out.println("Senha do servidor cadastrada: "+senhaLista.get(usuarioLista.indexOf(usuario)));
		}

		//Se usuario existir
		if( usuarioLista.indexOf(usuario) != -1 ) {
			//se a senha for válida
			if( senhaLista.get(usuarioLista.indexOf(usuario)).equals(senha) ) {
				//registra nova latitude e longitude do usuário para ele entrar no sistema:
				latitudeDoCliente.set(usuarioLista.indexOf(usuario), latitude);
				longitudeDoCliente.set(usuarioLista.indexOf(usuario), longitude);
				statusDoCliente.set(usuarioLista.indexOf(usuario), statusCliente);
				
				//Manda todos os outros clientes atualizarem suas telas principais

				atualizarTodos(usuario);
				return true;
			} else {
				return false;
			}
		} else {
			System.out.println("Usuário não encontrado!");
			return false;
		}

	}

	public boolean usuarioExistente(String usuario) {
		//Se usuario existir
		if( usuarioLista.indexOf(usuario) != -1 ) {
			return true;
		} else {
			return false;
		}
	}

	public String retornaNomeDadoUsuario(String usuario) {

		return nomeLista.get(usuarioLista.indexOf(usuario));

	}

	public String retornaFotoDadoUsuario(String usuario) {

		return fotoCliente.get(usuarioLista.indexOf(usuario));

	}

	//Obtém a distância entre dois pcs pela sua localização
	public double getDistancia(double lat1, double lon1, double lat2, double lon2){

		double cosS =  Math.sin(lat1)*Math.sin(lat2)+ Math.cos(lat1)*Math.cos(lat2)*Math.cos(lon1-lon2);
		double s = Math.acos(cosS);
		double raioDaTerra = 6378;

		double distancia = (int) (s*raioDaTerra);

		System.out.println("Distância: "+distancia);

		return distancia;
	}

	//Verifica se uma localização está na área especificada
	public boolean estaNaDistancia(double lat1, double lon1, double lat2, double lon2, double distancia){

		double distaciaEntrePontos = getDistancia(lat1, lon1, lat2, lon2);

		System.out.println("Distacia entre Pontos: "+distaciaEntrePontos+" distância passada: "+ distancia);

		//Se o ponto está na faixa especificada, ou seja,
		//é pro usuário ver
		if (distaciaEntrePontos<=distancia) {
			return true;
		} else {
			return false;
		}
	}
	/*
	//Obtém a lista com os nomes dos usuários que estão visíveis
	public ArrayList<String> listaUsuariosVisiveis(String nomeAtual, int lat, int lon, int distancia){

		ArrayList<String> listaDeUsuariosVisiveis = new ArrayList<String>();

		//tem que tirar o que está verificando

		for (int i = 0; i < usuarioLista.size(); i++) {

			boolean estaNaArea =getDistancia(lat, lon, latitudeDoCliente.get(i), longitudeDoCliente.get(i),distancia);

			if(estaNaArea){
				//Para o cliente que está buscando não ser adicionado na lista 
				if(!usuarioLista.get(i).equals(nomeAtual)){
					listaDeUsuariosVisiveis.add(usuarioLista.get(i));}
			}
		}
		return listaDeUsuariosVisiveis;

	}*/

	//Obtém a lista com os nomes dos usuários que estão visíveis
	public void listaUsuariosVisiveis(String nomeAtual, double lat, double lon, double distancia){

		ClienteInterface clien =referenciaDoCliente(nomeAtual);
		//tem que tirar o que está verificando

		//Remove todos os usuários que estão visíveis
		clien.removeTodosUsuariosVisiveis();
		clien.removeTodosFotoUsuariosVisiveis();
		clien.removeTodosLatitudeUsuariosVisiveis();
		clien.removeTodosLongitudeUsuariosVisiveis();
		clien.removeTodosStatusUsuariosVisiveis();

		for (int i = 0; i < usuarioLista.size(); i++) {

			boolean estaNaArea =estaNaDistancia(lat, lon, this.latitudeDoCliente.get(i), this.longitudeDoCliente.get(i),distancia);

			if(estaNaArea){
				System.out.println("Usuário: "+ usuarioLista.get(i)+" está visivel a "+nomeAtual);
				//Para o cliente que está buscando não ser adicionado na lista 
				if(!usuarioLista.get(i).equals(nomeAtual)){
					//adiciona a lista do usuário q está buscando quem
					//está visível
					clien.adicionarUsuarioVisivel(usuarioLista.get(i));
					clien.adicionarFotoUsuarioVisivel(fotoCliente.get(i));
					System.out.println("Usuário: "+usuarioLista.get(i)+", latitude: "+latitudeDoCliente.get(i));
					clien.adicionarLatitudeUsuarioVisivel(latitudeDoCliente.get(i));
					clien.adicionarLongitudeUsuarioVisivel(longitudeDoCliente.get(i));
					clien.adicionarStatusUsuarioVisivel(statusDoCliente.get(i));

				}
			}else{
				System.out.println("Usuário: "+ usuarioLista.get(i)+" não está visivel a "+nomeAtual);
			}

		}
	} 

	public void listaStatusUsuariosVisiveis(String login){
		ClienteInterface clien =referenciaDoCliente(login);
		//tem que tirar o que está verificando

		//Remove todos os usuários que estão visíveis
		clien.removeTodosStatusUsuariosVisiveis();

		for (int i = 0; i < statusDoCliente.size(); i++) {

			//adiciona a lista do usuário q está buscando quem
			//está visível
			clien.adicionarStatusUsuarioVisivel(statusDoCliente.get(i));	


		}
	}

	public void listaFotoUsuariosVisiveis(String login){
		ClienteInterface clien =referenciaDoCliente(login);
		//tem que tirar o que está verificando

		//Remove todos os usuários que estão visíveis
		clien.removeTodosFotoUsuariosVisiveis();

		for (int i = 0; i < fotoCliente.size(); i++) {

			//adiciona a lista do usuário q está buscando quem
			//está visível
			clien.adicionarFotoUsuarioVisivel(fotoCliente.get(i));	


		}
	}

	public void listaLongitudesUsuariosVisiveis(String login){
		ClienteInterface clien =referenciaDoCliente(login);
		//tem que tirar o que está verificando

		//Remove todos os usuários que estão visíveis
		clien.removeTodosLongitudeUsuariosVisiveis();

		for (int i = 0; i < longitudeDoCliente.size(); i++) {
			//adiciona a lista do usuário q está buscando quem
			//está visível
			clien.adicionarLongitudeUsuarioVisivel(longitudeDoCliente.get(i));
		}
	}

	public void listaLatitudesUsuariosVisiveis(String login){
		ClienteInterface clien =referenciaDoCliente(login);
		//tem que tirar o que está verificando

		//Remove todos os usuários que estão visíveis
		clien.removeTodosLatitudeUsuariosVisiveis();

		for (int i = 0; i < longitudeDoCliente.size(); i++) {
			//adiciona a lista do usuário q está buscando quem
			//está visível
			clien.adicionarLatitudeUsuarioVisivel(latitudeDoCliente.get(i));
		}
	}

	//Atualiza a tela principal de todos os clientes
	public void atualizarTodos(String nomeUsuarioQueRequisita){
		for (int i = 0; i < usuarioLista.size(); i++) {
			if(!statusDoCliente.get(i).equals("offline")&&!usuarioLista.get(i).equals(nomeUsuarioQueRequisita)){
				ClienteInterface clien =referenciaDoCliente(usuarioLista.get(i));
				clien.atualizaTelaPrincipal(nomeUsuarioQueRequisita);
			}
		}


	}
	
	
	public void setaUsuarioOffline(String login) {
		
		statusDoCliente.set(usuarioLista.indexOf(login), "offline");  
		
		atualizarTodos(login);
		
	}
	
	public void setaUsuarioStatus(String login, String status) {
		
		statusDoCliente.set(usuarioLista.indexOf(login), status);  
		
		atualizarTodos(login);
		
	}
	
	public void setaDistancia( String usuario, double latitude , double longitude ) {

		int indice =usuarioLista.indexOf(usuario);
		longitudeDoCliente.add(indice, longitude);
		latitudeDoCliente.add(indice, latitude);
		
		
	}
	
	
	
}
